package de.reichelt.fx;

import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.logging.Logger;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Control;
import javafx.scene.control.DatePicker;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;

public class FxFormField {
  
  private Logger log;
  private Control control;
  private String name;
  private boolean mandatory;
  private boolean editable;
  private SubType subType; //INTEGER, FLOAT, MONEY
  private List<FxValidator> validators;

  public enum SubType{INTEGER, DOUBLE}
  
  /**
   * definiert ein Datenfeld im Datenformular.
   * 
   * @param control  ist das Control-Element selbst, wird zum Zugriff auf Eigenschaften verwendet
   * @param name  ist der Name, über den das Formularfeld gesucht werden kann
   * @param mandatory  legt fest ob das Feld eine Pflichteingabe erwartet
   * @param editable  legt fest ob das Feld editierbar ist oder nur als Anzeige dienen soll
   */
  public FxFormField(Control control, String name, boolean mandatory, boolean editable) {
    log = Logger.getLogger(this.getClass().getName());
    log.fine("erzeuge Formularfeld");
    this.control = control;
    this.name = name;
    this.mandatory = mandatory;
    this.editable = editable;
    this.validators = new ArrayList<>();
  }

  /**
   * definiert ein Datenfeld im Datenformular.
   * 
   * @param control  ist das Control-Element selbst, wird zum Zugriff auf Eigenschaften verwendet
   * @param name  ist der Name, über den das Formularfeld gesucht werden kann
   * @param mandatory  legt fest ob das Feld eine Pflichteingabe erwartet
   * @param editable  legt fest ob das Feld editierbar ist oder nur als Anzeige dienen soll
   * @param subType  legt den Datentyp bei Textfeldern fest
   */
  public FxFormField(Control control, String name, boolean mandatory, boolean editable, 
          SubType subType) {
    this(control, name, mandatory, editable);
    this.subType = subType;
  }

  public boolean isMandatory() {
    return mandatory;
  }

  public void setMandatory(boolean mandatory) {
    this.mandatory = mandatory;
  }

  public boolean isEditable() {
    return editable;
  }

  public void setEditable(boolean editable) {
    this.editable = editable;
  }

  public Control getControl() {
    return control;
  }

  public void setControl(Control control) {
    this.control = control;
  }
  
  public List<FxValidator> getValidators() {
    return this.validators;
  }
  
  public void addValidator(FxValidator validator) {
    this.validators.add(validator);
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public SubType getSubType() {
    return subType;
  }

  public void setSubType(SubType subType) {
    this.subType = subType;
  }
  
  /**
   * liest den Wert des Formularfeldes; je nach Control-Typ wird der entsprechende 
   * Datentyp zurück gegeben;<br/>
   * TextField -> String<br/>
   * TextField, SubType.INTEGER -> Integer<br/>
   * TextField, SubType.DOUBLE -> Double<br/>
   * TextArea -> String
   * ComboBox -> Typ der ComboBox&lt;Typ&gt;<br/>
   * DatePicker -> Date<br/>
   * CheckBox -> Boolean
   * 
   * @return 
   */
  public Object getValue() throws Exception { 
    if (control!=null && control instanceof TextField) {
      String ret = ((TextField)control).getText();
      if (subType==SubType.INTEGER) {
        return (ret==null || ret.isEmpty())?(Integer)null:Integer.valueOf(ret);
      } else if (subType==SubType.DOUBLE) {
        return Double.valueOf(ret);
      } else {
        return ret;
      }
    } else if (control!=null && control instanceof TextArea) {
      return ((TextArea)control).getText();
    } else if (control!=null && control instanceof ComboBox) {
      return ((ComboBox)control).getValue();
    } else if (control!=null && control instanceof DatePicker) {
      LocalDate d = ((DatePicker)control).getValue();
      Instant instant = Instant.from(d.atStartOfDay(ZoneId.systemDefault()));
      return Date.from(instant);
    } else if (control!=null && control instanceof CheckBox) {
      Boolean ret = ((CheckBox)control).isSelected();
      return ret;
    } else {
      throw new RuntimeException("getValue() fuer unbekanntes Control aufgerufen!");
    }
  }
  
  /**
   * setzt den Wert des Formularfeldes; je nach Control-Typ wird der entsprechende 
   * Datentyp erwartet;<br/>
   * TextField -> String<br/>
   * TextField, SubType_INTEGER -> Integer<br/>
   * TextField, SubType_DOUBLE -> Double<br/>
   * TextArea -> String
   * ComboBox -> Typ der ComboBox&lt;Typ&gt;<br/>
   * DatePicker -> Date<br/>
   * CheckBox -> Boolean
   * 
   * @param value
   * @throws Exception 
   */
  public void setValue(Object value) throws Exception { 
    if (control!=null && control instanceof TextField) {
      ((TextField)control).setText((String)value);
    } else if (control!=null && control instanceof TextArea) {
      ((TextArea)control).setText((String)value);
    } else if (control!=null && control instanceof ComboBox) {
      ((ComboBox)control).setValue(value);
    } else if (control!=null && control instanceof DatePicker) {
      Instant instant = Instant.ofEpochMilli(((Date)value).getTime());
      LocalDate res = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()).toLocalDate();
      ((DatePicker)control).setValue(res);
    } else if (control!=null && control instanceof CheckBox) {
      ((CheckBox)control).setSelected((Boolean)value);
    }  else {
      throw new RuntimeException("setValue() fuer unbekanntes Control aufgerufen!");
    }
  }
  
  /**
   * prüft ob der Inhalt des Formularfeldes null oder leer ist, ob bei ComboBox kein 
   * Eintrag gewählt ist oder ob bei CheckBox der Wert Indeterminate gesetzt ist.
   * 
   * @return
   * @throws Exception 
   */
  public boolean isEmpty() throws Exception {
    if (control!=null && control instanceof TextField) {
      return ((TextField)control).getText()==null || ((TextField)control).getText().isEmpty();
    } else if (control!=null && control instanceof TextArea) {
      return ((TextArea)control).getText()==null || ((TextArea)control).getText().isEmpty();
    } else if (control!=null && control instanceof ComboBox) {
      return ((ComboBox)control).getValue()==null;
    } else if (control!=null && control instanceof DatePicker) {
      return ((DatePicker)control).getValue()==null;
    } else if (control!=null && control instanceof CheckBox) {
      return ((CheckBox)control).isIndeterminate();
    } else {
      throw new RuntimeException("isEmpty() fuer unbekanntes Control aufgerufen!");
    }
  }
  
}
